package com.isyscore.boot.login.web;

import com.isyscore.boot.login.LoginUserManager;
import com.isyscore.boot.login.LoginUserManagerImpl;
import com.isyscore.boot.login.properties.LoginProperties;
import com.isyscore.boot.login.util.CookieUtils;
import com.isyscore.device.common.exception.AuthenticationException;
import com.isyscore.device.common.exception.AuthorizationException;
import com.isyscore.os.permission.annotation.RequiredAcl;
import com.isyscore.os.permission.annotation.RequiredSuperAdmin;
import com.isyscore.os.permission.common.constants.PermissionConstants;
import com.isyscore.os.permission.entity.LoginVO;
import com.isyscore.os.permission.enums.AuthenticationSubjectType;
import com.isyscore.os.permission.exception.ErrorCode;
import com.isyscore.os.permission.exception.PermissionException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * @author wany
 * @since 2022-02-21
 */
@Slf4j
@RequiredArgsConstructor
public class LoginUserInterceptor extends HandlerInterceptorAdapter {

    private final LoginProperties loginProperties;

    private final LoginUserManager loginUserManager;

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
        if(loginUserManager.getAppLicenseStatus()) {
            LoginUserManagerImpl loginUserManagerImpl = (LoginUserManagerImpl) loginUserManager;
            AntPathMatcher antPathMatcher = new AntPathMatcher();
            for (String exclude : loginProperties.getExclude()) {
                if (antPathMatcher.match(exclude, request.getRequestURI())) {
                    return true;
                }
            }
            if (!loginProperties.isEnable()) {
                return true;
            }
            // 获取token
            String token = getToken(request);
            if (StringUtils.isEmpty(token)) {
                throw new AuthenticationException();
            }
            // 首先尝试从本地缓存中获取登录用户信息
            LoginVO currentLoginVo = loginUserManagerImpl.getCachedUserInfo(token);
            if (currentLoginVo == null) {
                // 尝试从permission服务中通过token获取登录信息
                currentLoginVo = loginUserManagerImpl.verifyToken(token, true);
                if (currentLoginVo != null) {
                    loginUserManagerImpl.addLoginUser2Cache(currentLoginVo);
                }
            } else if (currentLoginVo.getTokenExpiredTime() != null
                    && currentLoginVo.getTokenExpiredTime().isBefore(LocalDateTime.now())) {
                //如果取到的登录缓存信息已经过期，则认为token已经失效，需要重新登录
                loginUserManagerImpl.removeLoginUserCache(currentLoginVo.getToken());
                currentLoginVo = null;
            }
            if (currentLoginVo == null) {
                throw new AuthenticationException();
            }
            //设置当前登录用户信息
            loginUserManagerImpl.setThreadLocalLoginUser(currentLoginVo);
            //判断当前登录用户的类型
            if(AuthenticationSubjectType.application.equals(currentLoginVo.getSubjectType())){
                if(!currentLoginVo.isAuthedApp(loginProperties.getAppCode())){
                    throw new PermissionException(ErrorCode.APP_CODE_NOT_AUTHED);
                }
                String tenantId = request.getHeader(PermissionConstants.Header.CROSS_TENANT_TENANTID_KEY);
                if(!currentLoginVo.isAuthedTenant(tenantId)){
                    throw new PermissionException(ErrorCode.TENANT_NOT_AUTHED);
                }
            }else {
                // 根据API的注解验证当前用户的权限
                if (handler instanceof HandlerMethod) {
                    HandlerMethod handlerMethod = (HandlerMethod) handler;
                    RequiredSuperAdmin requiredSuperAdmin = handlerMethod.getMethodAnnotation(RequiredSuperAdmin.class);
                    if (requiredSuperAdmin != null && !currentLoginVo.isSuperAdmin()) {
                        throw new AuthorizationException();
                    }
                    RequiredAcl requiredAcl = handlerMethod.getMethodAnnotation(RequiredAcl.class);
                    if (requiredAcl != null) {
                        if (!currentLoginVo.isAdmin() && !currentLoginVo.isSuperAdmin() && !currentLoginVo.getPermission().containsAll(Arrays.asList(requiredAcl.value()))) {
                            throw new AuthorizationException();
                        }
                    }
                }
            }
        }else {
            throw new PermissionException(ErrorCode.LICENSE_INFO_ERROR);
        }
        return true;
    }

    @Override
    public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
        LoginUserManagerImpl loginUserManagerImpl = (LoginUserManagerImpl) loginUserManager;
        loginUserManagerImpl.removeThreadLocalLoginUser();
    }

    private String getToken(HttpServletRequest request) {
        // 从请求头获取
        String token = request.getHeader(PermissionConstants.Header.TOKEN);
        if (StringUtils.isNotEmpty(token)) {
            return token;
        }
        // 从请求参数中获取
        token = request.getParameter(PermissionConstants.Header.TOKEN);
        if (StringUtils.isNotEmpty(token)) {
            return token;
        }
        // 从cookie获取
        return CookieUtils.getCookieValue(request, PermissionConstants.Cookie.TENANT_COOKIE_NAME);
    }

}
